package xm.control
{
	import com.greensock.TweenLite;
	import com.greensock.easing.Cubic;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Stage;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	
	import xm.components.supportClasses.SkinnableComponent;
	import xm.control.bar.ActionBar;
	import xm.control.bar.TabBar;
	import xm.control.bar.barContent.ActionContent;
	import xm.control.bar.barContent.NavigationContent;
	import xm.control.bar.barContent.TabContent;
	import xm.control.bar.barContent.TitleContent;
	import xm.control.bar.base.BarBase;
	import xm.definition.*;
	import xm.events.XMStateEvent;
	import xm.manager.StageManager;
	import xm.view.ViewNavigatorApplication;
	
	/**
	 * <br> 클래스 설명 : XprimeMobile Framework 의 움직임을 관장하는 클래스</br>
	 * @author 김정환
	 */	
	public class Navigator
	{
		
		
		//-----------------------------------------------------------------------------------
		// singleTon Pattern
		//-----------------------------------------------------------------------------------
		private static var _instance : Navigator;
		

		/**
		 * @return StageManager
		 */		
		public static function getInstance():Navigator
		{
			return _instance;
		}
		
		/**
		 * @param app
		 */		
		public static function setInstance( app:ViewNavigatorApplication ):void
		{
			_instance = new Navigator( app );
		}
		
		
		//-----------------------------------------------------------------------------------
		// public vars
		//-----------------------------------------------------------------------------------
		
		
		
		
		//-----------------------------------------------------------------------------------
		// protected / internal vars
		//-----------------------------------------------------------------------------------
		
		
		
		
		//-----------------------------------------------------------------------------------
		// private vars
		//-----------------------------------------------------------------------------------
		
		private var _pageList : Vector.<SkinnableComponent> = new Vector.<SkinnableComponent>();
		private var _pageTypeList : Vector.<Class> = new Vector.<Class>();
		
		private var _app : ViewNavigatorApplication;
		private var _tabBar : TabBar;
		private var _actionBar : ActionBar;
		private var _currentView : SkinnableComponent;
		private var _customBarList : Array;
		private var _stageScaleMode : String;
		private var _isPrevDestroy : Boolean = false;
		
		private var _availableWidth : Number = 0;
		private var _availableHeight : Number = 0;
		private var _minY : Number = 0;
		private var _maxY : Number = 0;
		
		//-----------------------------------------------------------------------------------
		// public functions
		//-----------------------------------------------------------------------------------
		
		/**
		 * constructor 
		 */	
		public function Navigator( app:ViewNavigatorApplication )
		{
			if( app == null ) throw new Error( "Navigator 는 Application에서만 생성하여 사용할 수 있습니다." );
			_app = app;
			_app.addEventListener(XMStateEvent.STATE_APPLICATION_COMPLETE, onApplicationComplete );
			
			_tabBar = new TabBar();
			_actionBar = new ActionBar();
		}
			
		
		
		/**
		 * @param view 새로운 페이지 추가기능
		 */		
		public function add( view:Class, 
							  isTransition:Boolean = true ):void
		{
			_pageTypeList.push( view );
			
			var currentView : SkinnableComponent = new view() as SkinnableComponent;
			if( isTransition == false )
			{
				setCurrentView( currentView, isTransition );
			}
			else
			{
				if( currentView.isCreated ) setCurrentView( currentView );
				else currentView.addEventListener(XMStateEvent.STATE_CREATION_COMPLETE, 
					onCreationComplete );
			}
		}
		
		
		/**
		 * 이전으로 기능.
		 * <br />
		 * 페이지 리스트가 2개 이상일때 작동.
		 * <br /> 
		 */		
		public function removeCurrentView( isTransition:Boolean = true ):void
		{
			if( _pageTypeList.length > 1 )
			{
				delete _pageTypeList.pop();
				setCurrentView( _pageList[ _pageList.length - 2 ], isTransition );
			}
		}
		
		
		/**
		 * 정해진 숫자만큼 이전으로 기능.
		 * <br />
		 * 페이지 전체의 크기와 n 을 비교하여 더 적은 값만큼 이전으로 돌림.
		 * <br /> 
		 */		
		public function removeAtNum( n : int = 1, isTransition:Boolean = true ):void
		{
			var len:int = _pageTypeList.length-1,
				i : int = 0;
			
			if( len == 0 ) return;
			
			len = Math.min( len, n );
			
			while( i<len )
			{
				if( i == 0 )
				{
					delete _pageTypeList.pop();
				}
				else
				{
					delete _pageTypeList.pop();
					var view : SkinnableComponent = _pageList.pop().destroy();
				}
				
				++i;
			}
			
			setCurrentView( _pageList[ _pageList.length-2 ] );
		}
		
		/**
		 * 첫화면으로 가기 기능.
		 */		
		public function removeToFirstView():void
		{
			var len:int = _pageTypeList.length-1,
				i : int = 0,
				tempPage : Object;
			
			if( len == 0 ) return;
			
			while( i<len )
			{
				if( i == 0 )
				{
					delete _pageTypeList.pop();
					tempPage = _pageList.pop();
				}
				else
				{
					delete _pageTypeList.pop();
					var view : SkinnableComponent = _pageList.pop().destroy();
				}
				
				++i;
			}
			
			_pageList.push( tempPage );
			
			setCurrentView( _pageList[ 0 ] );
		}
		
		
		
		/**
		 * <br>트랜지션 명령</br>
		 * @param target : SkinnableComponent - 트랜지션 대상
		 * @param transitionStatus : String - TransitionStatus.IN, TransitionStatus.OUT
		 * @param transitionType : String - TransitionType.SLIDE ....
		 * @param direction : String - Direction.RIGHT, Direction.LEFT
		 * @param onComplete : Function - 콜백함수.
		 */		
		public function playTransition( target:SkinnableComponent, 
										transitionStatus:String, 
										transitionType:String, 
										direction:String, 
										onComplete:Function=null ):void
		{
			
			
			
			
			if( _stageScaleMode == StageScaleMode.EXACT_FIT )
			{
				
			}
			else
			{
				var s : Stage = StageManager.getInstance().stage,
					bd : BitmapData = new BitmapData( s.stageWidth, s.stageHeight ),
					bm : Bitmap;
				
				bd.draw( target );
				bm = new Bitmap( bd );
			}
			
			switch( transitionType )
			{
				case TransitionType.SLIDE :
					slide( target, ( _stageScaleMode == StageScaleMode.EXACT_FIT ) ? null : bm, transitionStatus, direction, onComplete );
					break;
			}
		}
		
		
		/**
		 * @return : Number - 사용가능한 너비
		 */		
		public function getAvailablePageWidth():Number
		{
			return _availableWidth;
		}
		
		
		/**
		 * @return : Number - 사용가능한 높이 ( 액션바와 탭바 높이를 제외한 값이 들어옴. )
		 */		
		public function getAvailablePageHeight():Number
		{
			return _availableHeight;
		}
		
		
		 /**
		  * 
		  * @param bar - add 하고자하는 CustomBar 객체
		  */		
		 public function addCustomBar( bar : BarBase ):void
		 {
			 if( _customBarList == null ) _customBarList = [];
			 _customBarList.push( bar );
			 bar.setStage( StageManager.getInstance().stage );
			 
			 calculateAvailableSize();
		 }
		 
		 /**
		  * @param bar - add 되어있는 CustomBar 객체
		  * @param isDestroy - 자동 제거여부.
		  */		 
		 public function removeCustomBar( bar : BarBase, isDestroy : Boolean = false ):void
		 {
			 var idx : int = _customBarList.indexOf( bar );
			 _customBarList.splice( idx, 1 );
			 bar.removeFromStage();
			 
			 if( isDestroy )
			 {
				 bar.destroy();
			 }
			 
			 calculateAvailableSize();
		 }
		 
		 /**
		  * 현재 추가되어있는 페이지중 index 에 해당하는 페이지 접근 API
		  * <br /> 
		  * <br /> 
		  * @param index : int
		  * @return : SkinnableComponent - View 를 상속받은 객체.
		  */		 
		 public function getPageAt( index : int ):SkinnableComponent
		 {
			 return _pageList[ index ];
		 }
		
		 
		 
		 /**
		  *	type에 해당하는 클래스들을 전부 반환.
		  * <br /> 
		  * <br /> 
		  * @param type
		  * @return 
		  */		 
		 public function getPageListCompareType( type:Class ):Array
		 {
			var i:int, len:int, arr : Array;
			
			i=0;
			len=_pageList.length;
			arr = [];
			
			while( i<len )
			{
				if( _pageList[ i ] as type )
				{
					arr.push( _pageList[ i ] );
				}
				++i;
			}
			
			return arr;
		 }
		
		 
		//-----------------------------------------------------------------------------------
		// setter / getter
		//-----------------------------------------------------------------------------------
		
		 /**
		  * 페이지 리스트의 길이
		  * @return 
		  */		 
		 public function get pageListLength():Number
		 {
			 return _pageList.length; 
		 }
		 
		/**
		 * @param content : NavigationContent
		 */		
		public function set navigationContent( content : NavigationContent ):void
		{
			_actionBar.setNavigationContent( content );
			calculateAvailableSize();
		}
		
		/**
		 * @param content : TitleContent
		 */		
		public function set titleContent( content : TitleContent ):void
		{
			_actionBar.setTitleContent( content );
			calculateAvailableSize();
		}
		
		/**
		 * @param content : ActionContent
		 */		
		public function set actionContent( content : ActionContent ):void
		{
			_actionBar.setActionContent( content );
			calculateAvailableSize();
		}
		
		/**
		 * @param content TabContent
		 */		
		public function set tabContent( content : TabContent ):void
		{
			_tabBar.setTabContent( content );
			calculateAvailableSize();
		}
		
		/**
		 * @return ActionBar;
		 */		
		public function get actionBar():ActionBar{ return _actionBar; }
		
		/**
		 * @return TabBar;
		 */		
		public function get tabBar():TabBar{ return _tabBar; }
		
		/**
		 * @return 현재 화면에 붙어있는 자식 view
		 */		
		public function get currentView():SkinnableComponent
		{
			return _currentView;
		}
		
		//-----------------------------------------------------------------------------------
		// protected functions
		//-----------------------------------------------------------------------------------
		
		
		
		//-----------------------------------------------------------------------------------
		// internal functions
		//-----------------------------------------------------------------------------------
		
		
		
		
		//-----------------------------------------------------------------------------------
		// private functions
		//-----------------------------------------------------------------------------------
		
		// 현재 뷰를 붙이기 전 transition 등을 실행하고, 이전 뷰를 처리하는 등 나머지 셋팅을 완료함.
		private function setCurrentView( currentView : SkinnableComponent, 
										 isTransition:Boolean = true ):void
		{
			_currentView = currentView;
			var preView : SkinnableComponent = ( _pageList.length > 0 ) ? _pageList[ _pageList.length - 1 ] : null;
			
			// 제거를 먼저하고, 스타트를 해줘야 높이를 
			if( preView != null ) preView.startRemoveTransition();
			currentView.startAddTransition();
			
			if( _pageTypeList.length > _pageList.length ) // 추가되는 트랜지션
			{
				// 1. 배열 셋팅
				_pageList.push( currentView );
				
				if( isTransition )
				{
					if( preView != null ) 
					{
						playTransition( preView, TransitionStatus.OUT, 
							TransitionType.SLIDE, Direction.LEFT, 
							onRemoveComplete );
						
						if( _stageScaleMode == StageScaleMode.EXACT_FIT )
						{
							
						}
						else
						{
							if( _app.contains( preView ) ) _app.removeChild( preView );
						}
					}
					
					playTransition( currentView, TransitionStatus.IN, 
						TransitionType.SLIDE, Direction.LEFT, 
						setCurrentViewComplete );
				}
				else
				{
					// 3. 화면셋팅
					setCurrentViewComplete(currentView);
				}
			}
			else // 제거되는 트랜지션
			{
				// preView 인 객체는 destory를 나중에 하기위해..
				_pageList.pop();
				
				// 배열셋팅
				var len:int = _pageList.length - _pageTypeList.length-1;
				for (var i:int = 0; i < len; i++) 
				{
					var view : SkinnableComponent = _pageList.pop();
					view.destroy();
				}
				
				// 2. 트랜지션
				if( isTransition )
				{
					playTransition( preView, TransitionStatus.OUT, 
						TransitionType.SLIDE, Direction.RIGHT, 
						onRemoveComplete );
					
					if( _stageScaleMode == StageScaleMode.EXACT_FIT )
					{
						_isPrevDestroy = true;
					}
					else
					{
						preView.destroy();
					}
					
					
					
					playTransition( currentView, TransitionStatus.IN, 
						TransitionType.SLIDE, Direction.RIGHT, 
						setCurrentViewComplete );
				}
				else
				{
					// 3. 화면셋팅
					setCurrentViewComplete(currentView);
				}
			}
			
		}
		
		// 화면에서 사라지는 트랜지션 끝난 후.
		private function onRemoveComplete( target : SkinnableComponent ):void
		{
			target.endRemoveTransition();
			
			if( _stageScaleMode == StageScaleMode.EXACT_FIT )
			{
				if( target.parent )
				{
					target.parent.removeChild( target );
				}
				
				if( _isPrevDestroy )
				{
					target.destroy();
				}
			}
			else
			{
				
			}
			
			_isPrevDestroy = false;
			
		}
		
		
		// 현재 뷰를 화면에 실제로 붙임.
		private function setCurrentViewComplete( target : SkinnableComponent = null ):void
		{
			if( target ) target.endAddTransition();
			if( _pageList[ _pageList.length-1 ] === target )
			{
				_app.addChild( target );
				target.y = _minY;
			}
		}
		
		private function getMask():Shape
		{
			var mask : Shape = new Shape();
			var g : Graphics = mask.graphics;
			var s : Stage = StageManager.getInstance().stage;
			
			g.beginFill( 0x000000, 1 );
			g.drawRect( 0,0, s.stageWidth, s.stageHeight );
			g.endFill();
			
			return mask;
		}
		/*
		- 좌우로 슬라이드되는 트랜지션
		*/
		private function slide( target:SkinnableComponent, bm : Bitmap, 
								transitionStatus:String, 
								direction:String, 
								onComplete:Function=null):void
		{
			var	destination : Number = 0,
				s : Stage = StageManager.getInstance().stage;
			
			if( _stageScaleMode == StageScaleMode.EXACT_FIT )
			{
				var mask : Shape = getMask();
				s.addChild( mask );
				target.mask = mask;
				
				s.addChildAt( target, 0 );
			}
			else
			{
				s.addChildAt( bm, 0 );
			}
			
			
			if( transitionStatus == TransitionStatus.IN )
			{
				if( _stageScaleMode == StageScaleMode.EXACT_FIT )
				{
					target.y = _minY;
				}
				else
				{
					bm.y = _minY;
				}
				
				if( direction == Direction.RIGHT )
				{
					if( _stageScaleMode == StageScaleMode.EXACT_FIT )
					{
						target.x = -s.stageWidth;
					}
					else
					{
						bm.x = -bm.width;
					}
					
					destination = 0;
				}
				else if( direction == Direction.LEFT )
				{
					if( _stageScaleMode == StageScaleMode.EXACT_FIT )
					{
						target.x = s.stageWidth;
					}
					else
					{
						bm.x = s.stageWidth;
					}
					destination = 0;
				}
			}
			else if( transitionStatus == TransitionStatus.OUT )
			{
				if( _stageScaleMode == StageScaleMode.EXACT_FIT )
				{
					target.y = _minY;
				}
				else
				{
					bm.y = _minY;
				}
				
				if( direction == Direction.RIGHT )
				{
					if( _stageScaleMode == StageScaleMode.EXACT_FIT )
					{
						destination = s.stageWidth;
					}
					else
					{
						bm.x = target.x;
						destination = s.stageWidth;
					}
				}
				else if( direction == Direction.LEFT )
				{
					if( _stageScaleMode == StageScaleMode.EXACT_FIT )
					{
						destination = -s.stageWidth;
					}
					else
					{
						bm.x = target.x;
						destination = -bm.width;
					}
				}
			}
			
			
			
			if( _stageScaleMode == StageScaleMode.EXACT_FIT )
			{
				mask.x = target.x;
				mask.y = target.y;
				
				TweenLite.to( target, 0.3, {x:destination, 
					onComplete:transitionPlayComplete, 
					onCompleteParams:[target, null, onComplete, mask], 
					onUpdate : transitionPlayUpdate,
					onUpdateParams : [ target, mask ],
					ease:Cubic.easeOut} );
			}
			else
			{
				TweenLite.to( bm, 0.3, {x:destination, 
					onComplete:transitionPlayComplete, 
					onCompleteParams:[target, bm, onComplete], 
					ease:Cubic.easeOut} ); 

			}
		}
		
		
		// transition 이 모두 끝났을 때 callback 함수 실행하고 dictionary에 담긴 변수 제거.
		private function transitionPlayUpdate( target:SkinnableComponent, 
												 mask:Shape ):void
		{
			mask.x = target.x;
			mask.y = target.y;
		}
		
		
		// transition 이 모두 끝났을 때 callback 함수 실행하고 dictionary에 담긴 변수 제거.
		private function transitionPlayComplete( target:SkinnableComponent, 
												 bm : Bitmap, 
												 onComplete:Function=null ,
												 mask:Shape = null
		):void
		{
			var s : Stage = StageManager.getInstance().stage;
			if( onComplete != null ) onComplete( target );
			
			
			if( _stageScaleMode == StageScaleMode.EXACT_FIT )
			{
				target.mask = null;
				s.removeChild( mask );
			}
			else
			{
				s.removeChild( bm );
			}
			
			
		}
		
		
		// availableSize resetting
		private function calculateAvailableSize():void
		{
			var s : Stage = StageManager.getInstance().stage,
				i:int = 0,
				len : int = 0;
			
			_minY = ( actionBar && s.contains( actionBar ) ) ? actionBar.y+actionBar.viewHeight : 0;
			_maxY = ( tabBar && s.contains( tabBar ) ) ? tabBar.y : s.stageHeight;
			
			if( _customBarList != null )
			{
				len = _customBarList.length;
				while( i<len )
				{
					var bar : BarBase = _customBarList[ i ] as BarBase;
					if( bar.align == Align.TOP )
					{
						_minY = Math.max( _minY, bar.y+bar.viewHeight );
					}
					else if( bar.align == Align.BOTTOM )
					{
						_maxY = Math.min( _maxY, bar.y );
					}
					
					++i;
				}
			}
			
			_availableWidth = s.stageWidth;
			_availableHeight = ( _maxY-_minY == 0 ) ? s.stageHeight : Math.min( _maxY-_minY, s.stageHeight );
			
//			if( _customBarList && _customBarList.length > 0 )
//			{
//				var i:int=0,
//					len:int = _customBarList.length,
//					sum : Number = 0;
//				
//				while( i<len )
//				{
//					sum += _customBarList[ i ].height;
//					++i;
//				}
//				
//				_availableHeight -= sum;
//			}
		}
		
		//-----------------------------------------------------------------------------------
		// event handler
		//-----------------------------------------------------------------------------------
		
		
		/**
		 * application 의 생성완료시점.
		 * @param e
		 */		
		protected function onApplicationComplete(e:XMStateEvent):void
		{
			var s : Stage = StageManager.getInstance().stage;
			_actionBar.setStage( s );
			_tabBar.setStage( s );
			
			calculateAvailableSize();
			s.addEventListener(Event.RESIZE, onStageResize );
			
			trace(" ==> Navigator onApplicationComplete : stage scaleMode is " + s.scaleMode );
			_stageScaleMode = s.scaleMode;
			
		}	
		
		
		/**
		 * @param e
		 */		
		protected function onStageResize(e:Event):void
		{
			calculateAvailableSize();
		}
		
		/**
		 * child 들의 생성완료 시점.
		 * @param e
		 */		
		protected function onCreationComplete(e:Event):void
		{
			e.currentTarget.removeEventListener( e.type, arguments.callee, e.bubbles );
			var currentView : SkinnableComponent = e.currentTarget as SkinnableComponent;
			setCurrentView( currentView );
		}
		
		
		
	}
}









